#include "aperturetemplate.h"

#include <QtMath>
#include <QDebug>
#include <QTransform>

namespace LeGerber
{

	ApertureTemplate::ApertureTemplate(const QString &name, const QString &friendlyName, bool isBuiltIn):
	    name(name), friendlyName(friendlyName), isBuiltIn(isBuiltIn)
	{

	}

	ApertureTemplate::~ApertureTemplate()
	{

	}


	CircleApertureTemplate::CircleApertureTemplate():
	    ApertureTemplate("C", "Circle", true)
	{

	}

	Aperture *CircleApertureTemplate::instanciate(const QList<qreal> parameters) const
	{
		if (parameters.count() < 1 || parameters.count() > 2)
		{
			return nullptr;
		}

		auto result = new Aperture;
		result->apertureTemplate = this;

		qreal radius = parameters.at(0)/2.0;
		result->shape.addEllipse(QPointF(0, 0), radius, radius);

		if (parameters.count() >= 2)
		{
			QPainterPath hole;
			qreal holeRadius = parameters.at(1)/2.0;
			hole.addEllipse(QPointF(0, 0), holeRadius, holeRadius);
			result->shape -= hole;
		}

		result->width = 2.0*radius;

		return result;
	}

	RectApertureTemplate::RectApertureTemplate():
	    ApertureTemplate("R", "Rectangle", true)
	{

	}

	Aperture *RectApertureTemplate::instanciate(const QList<qreal> parameters) const
	{
		if (parameters.count() < 2 || parameters.count() > 3)
		{
			return nullptr;
		}

		auto result = new Aperture;
		result->apertureTemplate = this;

		qreal width = parameters.at(0);
		qreal height = parameters.at(1);
		result->shape.addRect(-width/2.0, -height/2.0, width, height);

		if (parameters.count() == 3)
		{
			QPainterPath hole;
			qreal holeRadius = parameters.at(2)/2.0;
			hole.addEllipse(QPointF(0, 0), holeRadius, holeRadius);
			result->shape -= hole;
		}

		result->width = width;

		return result;
	}

	ObroundApertureTemplate::ObroundApertureTemplate():
	    ApertureTemplate("O", "Obround", true)
	{
	}

	Aperture *ObroundApertureTemplate::instanciate(const QList<qreal> parameters) const
	{
		if (parameters.count() < 2 || parameters.count() > 3)
		{
			return nullptr;
		}

		auto result = new Aperture;
		result->apertureTemplate = this;

		qreal width = parameters.at(0);
		qreal height = parameters.at(1);
		result->shape.addRect(-width/2.0, -height/2.0, width, height);

		QPainterPath round;
		qreal radius = qMin(width, height)/2.0;
		round.addEllipse(0, 0, radius, radius);
		if (width > height)
		{
			result->shape += round.translated(width/2.0, 0);
			result->shape += round.translated(-width/2.0, 0);
		}
		else
		{
			result->shape += round.translated(0, height/2.0);
			result->shape += round.translated(0, -height/2.0);
		}

		if (parameters.count() == 3)
		{
			QPainterPath hole;
			qreal holeRadius = parameters.at(2)/2.0;
			hole.addEllipse(QPointF(0, 0), holeRadius, holeRadius);
			result->shape -= hole;
		}

		return result;
	}

	PolygonApertureTemplate::PolygonApertureTemplate():
	    ApertureTemplate("P", "Polygon", true)
	{

	}

	Aperture *PolygonApertureTemplate::instanciate(const QList<qreal> parameters) const
	{
		if (parameters.count() < 2 || parameters.count() > 4)
		{
			return nullptr;
		}

		auto result = new Aperture;
		result->apertureTemplate = this;

		qreal radius = parameters.at(0)/2.0;
		int edges = qRound(qAbs(parameters.at(1)));
		qreal rotation = 0;
		if (parameters.count() >= 3)
			rotation = parameters.at(2) * 10.0;
		QPolygonF polygon;
		for (int i = 0; i < edges; i++)
		{
			qreal alpha = qDegreesToRadians(i*(360.0/edges) + rotation);
			qreal x = radius*qCos(alpha);
			qreal y = radius*qSin(alpha);
			polygon.append(QPointF(x, y));
		}
		result->shape.addPolygon(polygon);
		result->shape.closeSubpath();

		if (parameters.count() == 4)
		{
			QPainterPath hole;
			qreal holeRadius = parameters.at(3)/2.0;
			hole.addEllipse(QPointF(0, 0), holeRadius, holeRadius);
			result->shape -= hole;
		}

		return result;
	}

	CustomApertureTemplate::CustomApertureTemplate(const QString &name, QList<AperturePrimitive> primitives):
	    ApertureTemplate (name),
	    primitives(primitives)
	{

	}

	CustomApertureTemplate::~CustomApertureTemplate()
	{

	}

	Aperture *CustomApertureTemplate::instanciate(const QList<qreal> parameters) const
	{
		if (!parameters.isEmpty())
			return nullptr;

		QPainterPath result;
		for (auto primitive: primitives)
		{
			qreal rotation = 0.0;
			bool exposure = primitive.exposure;
			QPainterPath path;
			switch (primitive.type)
			{
				case 1: // circle
				{
					if (primitive.modifiers.count() == 4)
						rotation = primitive.modifiers.at(3);
					else if (primitive.modifiers.count() != 3)
						return nullptr;
					qreal diameter = primitive.modifiers.at(0);
					QPointF center(primitive.modifiers.at(1), primitive.modifiers.at(2));
					QRectF rect(center.x() - diameter/2.0, center.y() - diameter/2.0,
					            diameter, diameter);
					path.addEllipse(rect);
					break;
				}
				case 4: // outline
					break;
				case 5: // polygon
					break;
				case 6: // moiré
					return nullptr;
				case 7: // thermal
				{
					if (primitive.modifiers.count() == 6)
						rotation = primitive.modifiers.at(5);
					else if (primitive.modifiers.count() != 5)
						return nullptr;
					QPointF center(primitive.modifiers.at(0), primitive.modifiers.at(1));
					qreal outDiameter = primitive.modifiers.at(2);
					qreal inDiameter = primitive.modifiers.at(3);
					qreal gap = primitive.modifiers.at(4);
					QRectF outRect(center.x() - outDiameter/2.0, center.y() - outDiameter/2.0,
					               outDiameter, outDiameter);
					QRectF inRect(center.x() - inDiameter/2.0, center.y() - inDiameter/2.0,
					              inDiameter, inDiameter);
					QRectF hBar(center.x() - outDiameter/2.0, -gap/2.0,
					            outDiameter, gap);
					QRectF vBar(-gap/2.0, center.y() - outDiameter/2.0,
					            gap, outDiameter);
					QPainterPath outPath;
					outPath.addEllipse(outRect);
					QPainterPath inPath;
					inPath.addEllipse(inRect);
					QPainterPath cross;
					cross.addRect(hBar);
					cross.addRect(vBar);
					path = outPath - inPath - cross;
					break;
				}
				case 20: // vector line
				{
					if (primitive.modifiers.count() == 6)
						rotation = primitive.modifiers.at(5);
					else if (primitive.modifiers.count() != 5)
						return nullptr;
					qreal thickness = primitive.modifiers.at(0);
					qreal xStart = primitive.modifiers.at(1);
					qreal yStart = primitive.modifiers.at(2);
					qreal xEnd = primitive.modifiers.at(3);
					qreal yEnd = primitive.modifiers.at(4);
					QPointF topLeft(xStart,
					                yStart - thickness/2.0);
					QPointF bottomRight(xEnd,
					                    yEnd - thickness/2.0);
					path.addRect(QRectF(topLeft, bottomRight));
					break;
				}
				case 21: // center line
				{
					if (primitive.modifiers.count() == 5)
						rotation = primitive.modifiers.at(4);
					else if (primitive.modifiers.count() != 4)
						return nullptr;
					QSizeF size(primitive.modifiers.at(0), primitive.modifiers.at(1));
					QPointF topLeft(primitive.modifiers.at(2) - size.width()/2.0,
					                primitive.modifiers.at(3) - size.height()/2.0);
					path.addRect(QRectF(topLeft, size));
					break;
				}
				default:
					return nullptr;
			}

			if (!qFuzzyCompare(rotation, 0.0) && !qFuzzyCompare(rotation, 360.0))
			{
				QTransform xform;
				xform.rotate(rotation);
				path = xform.map(path);
			}
			if (exposure)
				result += path;
			else
				result -= path;
		}
		Aperture *aperture = new Aperture;
		aperture->apertureTemplate = this;
		aperture->shape = result;
		return aperture;
	}

    AperturePrimitive::AperturePrimitive():
        type(-1), exposure(true)
    {

    }

    AperturePrimitive::AperturePrimitive(int type, bool exposure, QList<qreal> modifiers):
	    type(type), exposure(exposure), modifiers(modifiers)
	{

	}

}